﻿//  --------------------------------
//  Copyright (c) AW Systems, Inc. All rights reserved.
//  This source code is made available under the terms of the Microsoft Public License (Ms-PL)
//  http://www.codeplex.com/SLAM/license
//  ---------------------------------

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using AWS.SLAM.Configuration;
using AWS.SLAM.Data;
using AWS.SLAM.Logging;
using AWS.SLAM.SharePoint;
using Microsoft.SharePoint;

namespace AWS.SLAM.Events
{
    /// <summary>
    /// ListItemUpdater updates an item to SLAM's datasource(s) when a new List Item is updated in a 
    /// List configured to be managed by SLAM in SharePoint.
    /// 
    /// ListItemUpdater is the default implementation of SLAM's IItemUpdater that uses a List Item's
    /// List to determine its behavior.
    /// </summary>
    public class ListItemUpdater : IItemUpdater
    {
        #region Private State 
       
        private IDataServices _dataServices;
        private IConfigurationManager _configuration;        
        private ILogger _logger;

        /// <summary>
        /// Regex pattern to validate Guid strings
        /// </summary>
        private static Regex _guidRegex = new Regex(@"^(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}$", RegexOptions.Compiled);

        #endregion

        #region Constructors

        public ListItemUpdater(IDataServices dataServices, IConfigurationManager configuration, ILogger logger)
        {
            _dataServices = dataServices;
            _configuration = configuration;            
            _logger = logger;            
        }

        #endregion

        #region IItemUpdater Members

        public virtual bool Update(SPListItem listItem)
        {   
            bool isInError = false;
            string parentListId = listItem.ParentList.ID.ToString();
            
            try
            {
                string logItemInformation = listItem.ParentList.Title + Environment.NewLine + " List: " + listItem.ParentList.Title + Environment.NewLine + "ListItem: " + listItem.GetTitle() + ", " + listItem.UniqueId.ToString();
                _logger.LogMessage("Information", "Update LI Start - " + logItemInformation);

                string itemTypeName = _configuration.DataMapping.GetTypeAttribute(parentListId, listItem.ParentList.Title, "TableName");
                if (String.IsNullOrEmpty(itemTypeName))
                {
                    itemTypeName = listItem.ParentList.Title;
                }

                _dataServices.Update(itemTypeName, listItem.UniqueId.ToString(), listItem.GetFieldValues(_configuration.DataMapping.GetTypeFields(parentListId, itemTypeName) as List<Field>));

                _logger.LogMessage("Information", "Update LI Complete - " + logItemInformation);

                List<string> clearedAssociations = new List<string>();

                _logger.LogMessage("Information", "Update LI Associations - " + logItemInformation);

                foreach (AssociationField association in _configuration.DataMapping.GetTypeAssociations(parentListId, listItem.ParentList.Title))
                {
                    if (listItem.Fields[association.Name] != null)
                    {
                        if (!clearedAssociations.Contains(association.AssociationName))
                        {
                            _logger.LogMessage("Information", "Update LI Delete Associations - For: " + itemTypeName + " Type: " + association.AssociationName);
                            _dataServices.DeleteAllAssociations(association.AssociationName, itemTypeName, listItem.UniqueId.ToString());
                            clearedAssociations.Add(association.AssociationName);
                        }

                        switch (listItem.Fields[association.Name].Type)
                        {
                            case SPFieldType.Invalid:
                            case SPFieldType.Lookup:
                            
                                Guid listId = Guid.Empty;
                                PropertyDescriptor property = TypeDescriptor.GetProperties(listItem.Fields[association.Name]).Find("LookupList", false);

                                if (property != null && property.PropertyType == typeof(string))
                                {
                                    string id = property.GetValue(listItem.Fields[association.Name]).ToString();
                                    if (_guidRegex.IsMatch(id))
                                    {
                                        listId = new Guid(id);
                                    }
                                }

                                if (listId != Guid.Empty)
                                {
                                    SPList lookupList = null;
                                    foreach (SPWeb otherSite in listItem.Web.Site.AllWebs)
                                    {
                                        try
                                        {
                                            lookupList = otherSite.Lists[listId];
                                            if (lookupList != null)
                                            {
                                                otherSite.Dispose();
                                                break;
                                            }
                                        }
                                        catch
                                        {
                                            //List is not found
                                        }
                                        otherSite.Dispose();
                                    }

                                    if (listItem[association.Name] is SPFieldLookupValueCollection)
                                    {
                                        foreach (SPFieldLookupValue lookupField in (SPFieldLookupValueCollection)listItem[association.Name])
                                        {
                                            if (!String.IsNullOrEmpty(lookupField.LookupValue))
                                            {
                                                string associatedListItemId = String.Empty;
                                                try
                                                {
                                                    associatedListItemId = lookupList.GetItemById(lookupField.LookupId).UniqueId.ToString();
                                                }

                                                catch (ArgumentException)
                                                {
                                                    //The non-existence of an item can only be deteremined
                                                    //when SharePoint throws an exception, there is no check method
                                                    //nor null return value for GetItemById
                                                }

                                                _logger.LogMessage("Information", "Update LI Association - Type: " + association.AssociationName + " From: " + itemTypeName + " To: " + association.AssociatedTypeName);

                                                _dataServices.Associate(association.AssociationName, itemTypeName, listItem.UniqueId.ToString(), association.AssociatedTypeName, associatedListItemId);
                                            }
                                        }
                                    }
                                    else if (listItem[association.Name] != null && listItem[association.Name].ToString() != String.Empty)
                                    {
                                        SPFieldLookupValue lookupField = new SPFieldLookupValue(listItem[association.Name].ToString());

                                        if (!String.IsNullOrEmpty(lookupField.LookupValue))
                                        {
                                            string associatedListItemId = String.Empty;
                                            try
                                            {
                                                associatedListItemId = lookupList.GetItemById(lookupField.LookupId).UniqueId.ToString();
                                            }
                                            catch (ArgumentException argException)
                                            {
                                                //The non-existence of an item can only be deteremined
                                                //when SharePoint throws an exception, there is no check method
                                                //nor null return value for GetItemById
                                            }

                                            _logger.LogMessage("Information", "Update LI Association - Type: " + association.AssociationName + " From: " + itemTypeName + " To: " + association.AssociatedTypeName);
                                            _dataServices.Associate(association.AssociationName, itemTypeName, listItem.UniqueId.ToString(), association.AssociatedTypeName, associatedListItemId);
                                        }
                                    }
                                }
                                break;
                            case SPFieldType.User:
                                SPList userLookupList = listItem.Web.SiteUserInfoList;
                                if (listItem[association.Name] is SPFieldUserValueCollection)
                                {
                                    foreach (SPFieldUserValue userField in (SPFieldUserValueCollection)listItem[association.Name])
                                    {
                                        if (!String.IsNullOrEmpty(userField.LookupValue))
                                        {
                                            _logger.LogMessage("Information", "Update LI Association - Type: " + association.AssociationName + " From: " + itemTypeName + " To: " + association.AssociatedTypeName);
                                            _dataServices.Associate(association.AssociationName, itemTypeName, listItem.UniqueId.ToString(), association.AssociatedTypeName, userLookupList.GetItemById(userField.LookupId).UniqueId.ToString());
                                        }
                                    }
                                }
                                else if (listItem[association.Name] != null && listItem[association.Name].ToString() != String.Empty)
                                {
                                    SPFieldUserValue userField = new SPFieldUserValue(listItem.Web, listItem[association.Name].ToString());

                                    if (!String.IsNullOrEmpty(userField.LookupValue))
                                    {
                                        string associatedListItemId = String.Empty;
                                        try
                                        {
                                            associatedListItemId = userLookupList.GetItemById(userField.LookupId).UniqueId.ToString();
                                        }
                                        catch (ArgumentException)
                                        {
                                            //The non-existence of an item can only be deteremined
                                            //when SharePoint throws an exception, there is no check method
                                            //nor null return value for GetItemById
                                        }
                                        _logger.LogMessage("Information", "Update LI Association - Type: " + association.AssociationName + " From: " + itemTypeName + " To: " + association.AssociatedTypeName);
                                        _dataServices.Associate(association.AssociationName, itemTypeName, listItem.UniqueId.ToString(), association.AssociatedTypeName, associatedListItemId);
                                    }
                                }
                                break;
                        }
                    }
                }
                _logger.LogMessage("Information", "Update LI Associations Complete - " + logItemInformation);
            }
            catch (Exception ex)
            {
                _logger.LogMessage("Exception", ex.ToString());
                isInError = true;
            }

            _logger.LogEvent("Update", listItem.ParentList.Title, "List: " + listItem.ParentList.Title + Environment.NewLine + "ListItem: " + listItem.GetTitle() + ", " + listItem.UniqueId.ToString());

            return !isInError;
        }

        #endregion
    }
}
